/**
 * Project: PWS_GSM
 * Created Date: Monday, June 4th 2018, 7:43:43 pm
 * Author: Thomas.Li
 * E-Mail: leeyinghui@hotmail.com
 * Intro: Modem class
 * -----
 * Last Modified: Wed Jun 13 2018
 * Modified By: Thomas.Li
 * -----
 * Copyright (c) 2018 pareact
 * ------------------------------------
 * Always bet on Javascript!
 */
const SerialPort = require('serialport');
const async = require('async');
const pduParser = require('./pdu');

let defaults = {
  timeout: 15000,
  parity: 'none', //校验
  dataBits: 8,
  stopBits: 1,
  baudRate: 9600,
  // rtscts: true,
  autoOpen: true,
};

const regexp = /(\r?(.+)\r)?\r\n(.+)\r\n$/;//初始化正则
const cmdRegexp = /(\r?(.+)\r)?(\r\n([\s\S]*)\r\n)?\r\n([A-Za-z]+)\r\n$/;//发送命令正则

class Modem extends SerialPort {
  constructor(url, opt) {
    for (var k in opt)
      defaults[k] = opt[k];

    super(url, defaults, (err) => {
      if (err) {
        throw new Error(err);
      } else {
        console.log('connected ok!');
      }
    });

    this.options = defaults;
    this.buf = ''; //缓冲区
    this.sendSta = false;//发送状态
    this.hasData = true;//是否有返回数据，而非只有结果


    this.on('error', function (err) {
      throw new Error(err);
    });

    this.on('readable', () => {
      this.buf += this.read();
      // console.log(this.sendSta);
      // console.log(Buffer.from(this.buf));
      // console.log(this.sendSta + ':' + this.buf);
      if (this.sendSta) {
        if (/>/.test(this.buf)) {
          this.emit('message', ['OK', this.buf]);
          this.buf = '';
        }
        if (cmdRegexp.test(this.buf)) {
          var m = cmdRegexp.exec(this.buf);
          this.emit('message', [m[5], m[4]]);
          this.buf = '';
        }
      }
      else {
        // console.log(this.buf);
        if (regexp.test(this.buf)) {
          var m = regexp.exec(this.buf);
          this.emit('inComingMessage', m[3]);
          this.buf = '';
        }
      }
    });

    this.on('inComingMessage', mes => {
      var p = mes.split(/:\s?/);
      if (p.length === 2) {
        this.emit(p[0], p[1])
      } else {
        console.log('value:' + mes);
      }
    });

    this.queue = async.queue((task, done) => {
      this.sendSta = true;
      this.clear();
      this.write(task.data + '\r', () => this.drain());
      function onMessage(message) {
        clearTimeout(this.timeout);
        if (message[0] == 'OK') {
          task.accept(message[1] ? message[1] : message[0]);
        } else {
          task.reject(message[1]);
        }
        this.removeListener('message', onMessage);
        done();
      }
      this.on('message', onMessage);
      // to temporary disable timeout use: null, 0, false
      if (this.options.timeout) {
        this.timeout = setTimeout(() => {
          task.reject(new Error('Sexy Timeout exceeded'));
          this.removeListener('message', onMessage);
          done();
        }, +this.options.timeout);
      }
    });

    this.queue.drain = () => {
      this.sendSta = false;
      console.log('Send work has been finished!');
    }
  }

  // isOpen() { return this.port.isOpen }
  clear() { this.buf = '' }

  send(data) {
    let command = Object.assign({ data }, {});
    command.promise = new Promise((accept, reject) => {
      command.accept = accept;
      command.reject = reject;
    });
    this.queue.push(command);
    return command.promise;
  };

  //查询
  test(cmd) {
    return this.send(`AT+${cmd}?`);
  };

  //执行并返回值
  exec(cmd) {
    return this.send(`AT+${cmd}`);
  };

  //设置或读取短信
  set(name, value) {
    return this.send(`AT+${name}=${value}`);
  };

  //查询并返回值
  get(name) {
    return this.send(`AT+${name}=?`);
  };

  async imei() {
    return this.exec('CGSN');
  }

  async imsi() {
    return this.exec('CIMI');
  }

  async sms_center() {
    return new Promise((resolve, reject) => {
      this.test('CSCA').then(res => {
        var p = res.split(/\"/);
        if (p.length === 3) {
          resolve(p[1]);
        } else {
          reject('ERROR');
        }
      })
    });
  }

  async signal_length() {
    return new Promise((resolve, reject) => {
      this.exec('CSQ').then(res => {
        var p = res.split(/:\s?/);
        if (p.length === 2) {
          resolve(p[1]);
        } else {
          reject('ERROR');
        }
      });
    })
  }

  async sms_read(index) {
    return new Promise((resolve, reject) => {
      this.set('CMGR', index).then(res => {
        try {
          let ss = res.split(/\r\n/);
          resolve(pduParser.parse(ss[1]));
        } catch (error) {
          reject(error);
        }
      });
    })
  }

  async sms_read_all(index) {
    return new Promise((resolve, reject) => {
      this.set('CMGL', index).then(res => {
        try {
          let ss = res.split(/\+CMGL/ig);
          let result = [];
          ss.forEach(element => {
            if (element.length > 5) {
              let si = element.split(/\r\n/);
              result.push(pduParser.parse(si[1]))
            }
          });
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
    })
  }

  async sms_delete(index) {
    return this.set('CMGD', index);
  }

  async sms_mode(mode) {
    return this.set('CMGF', mode || 0);
  };

  async set_sms_center(mode) {
    return this.set('CSCA', mode || '+8613800100500');
  };

  async set_gsm() {
    return this.set('CSCS', "GSM");
  };

  async sms_simple_send(number, content) {
    return new Promise((resolve, reject) => {
      this.set('CMGS', `"${number}"`).then(res => {
        this.send(content + '\u001a').then(res => resolve(res)).catch(e => reject(e));
      }).catch(e => reject(e));
    })
  };

  async sms_send(number, content, sc) {
    let addr = sc.replace('+', '') + 'F';
    addr = '0891' + Buffer.from(addr, 'hex').map(bt => bt % 16 * 16 + (bt >> 4)).toString('hex');
    let phone = number.replace('+', '') + 'F';
    phone = '11000D91' + Buffer.from(phone, 'hex').map(bt => bt % 16 * 16 + (bt >> 4)).toString('hex');
    let msg = content.split('').map(c => c.charCodeAt(0).toString(16).padStart(4, '0')).join('');
    msg = (msg.length / 2).toString(16).padStart(2, '0') + msg;
    phone = phone + '000800' + msg;

    let count = (phone.length / 2);
    return new Promise((resolve, reject) => {
      this.set('CMGS', `${count}`).then(res => {
        this.send(addr + phone + '\u001a').then(res => resolve(res)).catch(e => reject(e));
      }).catch(e => {
        reject(e);
      });
    })
  }
  async request() {
    return new Promise((resolve, reject) => {
      this.set('HTTPACTION', 0).then(res => {
        resolve(res);
      }).catch(e => reject(e));
    })
  };

  async get_request() {
    return new Promise((resolve, reject) => {
      this.exec('HTTPREAD').then(res => {
        resolve(res);
      }).catch(e => reject(e));
    })
  };

}
module.exports = Modem;